package com.xiaoyu.im.provider;

import com.xiaoyu.base.DBCall;
import com.xiaoyu.base.data.UserData;
import com.xiaoyu.im.datamodel.AppMessage;
import com.xiaoyu.im.datamodel.AppMessageReceipt;

import in.srain.cube.util.CheckUtil;
import in.srain.cube.concurrent.AppThreads;
import in.srain.cube.util.internal.AppConsumer2;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class MessageReceiptData {

    private static final MessageReceiptData INSTANCE = new MessageReceiptData();

    private final ConcurrentHashMap<String, Long> mLatestReceiptTimeMap = new ConcurrentHashMap<>();

    private final Object mLock = new Object();

    public static MessageReceiptData getInstance() {
        return INSTANCE;
    }

    public void clear() {
        synchronized (mLock) {
            mLatestReceiptTimeMap.clear();
        }
    }

    public boolean isReceiptTime(String chatId, long time) {
        CheckUtil.checkNotMain();

        synchronized (mLock) {
            Long latestReceiptTime = mLatestReceiptTimeMap.get(chatId);
            return latestReceiptTime != null && latestReceiptTime >= time;
        }
    }

    void updateOnMessageSent(AppMessage pendingMessage, AppMessage successMessage, AppConsumer2<AppMessage, AppMessage> onMessageSentFunction) {
        CheckUtil.checkNotMain();

        final String chatId = successMessage.chatToken.getChatId();

        synchronized (mLock) {
            if (isReceiptTime(chatId, successMessage.time)) {
                successMessage.setReadReceipt();
            }
            DBCall.run(database -> database.messageDao().upsert(successMessage.toEntity()));

            if (onMessageSentFunction != null) {
                onMessageSentFunction.accept(pendingMessage, successMessage);
            }
        }
    }

    void updateOnMessageReceipt(List<AppMessageReceipt> appMessageReceipts, AppConsumer2<String, Long> updateConsumer) {
        final String uid = UserData.getInstance().getUid();
        AppThreads.runOnIOThread(() -> updateOnMessageReceiptInternal(uid, updateConsumer, appMessageReceipts));
    }

    private void updateOnMessageReceiptInternal(String uid, AppConsumer2<String, Long> updateConsumer, List<AppMessageReceipt> appMessageReceipts) {
        synchronized (mLock) {
            for (AppMessageReceipt messageReceipt : appMessageReceipts) {
                updateLatestTimeMap(messageReceipt.chatId, messageReceipt.time);
                updateReceiptInMemory(messageReceipt.chatId, messageReceipt.time, updateConsumer);
                updateReceiptInDB(uid, messageReceipt.chatId, messageReceipt.time);
            }
        }
    }

    private void updateLatestTimeMap(String chatId, long time) {
        Long oldTime = mLatestReceiptTimeMap.get(chatId);
        if (oldTime == null || oldTime < time) {
            mLatestReceiptTimeMap.put(chatId, time);
        }
    }

    private void updateReceiptInMemory(String chatId, long time, AppConsumer2<String, Long> updateConsumer) {
        updateConsumer.accept(chatId, time);
    }

    private void updateReceiptInDB(String uid, String chatId, long time) {
        DBCall.run(uid, database -> database.messageDao().updateReadReceipt(chatId, uid, time));
    }
}
